package net.cathackers.devel.scmp.chat.impl.sockets;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.executor.OrderedThreadPoolExecutor;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import net.cathackers.devel.scmp.chat.impl.xmpp.XMPPProtocolCodecFactory;
import net.cathackers.devel.scmp.chat.impl.xmpp.ConnectionHandler;
import net.cathackers.devel.scmp.chat.interfaces.ChatConfiguration;
import net.cathackers.devel.scmp.chat.interfaces.ChatManager;
import net.cathackers.devel.scmp.chat.interfaces.LobbyUser;
import net.cathackers.devel.scmp.chat.interfaces.Protocol;
import net.cathackers.devel.scmp.chat.interfaces.sockets.SocketServer;
import net.cathackers.devel.scmp.factories.SCMPFactory;

/**
 * Super Simple implementation of SocketServer that dispatch new Lobby User for
 * each new user connected and uses a factory to create the correct lobby user
 * implementation according to the protocol
 * 
 * @author Ahmed Soliman
 * 
 */
public class SocketServerImpl implements SocketServer {
    private List<LobbyUser> lobbyUsers_;
    private ChatConfiguration config_;
    NioSocketAcceptor acceptor_;
    ConnectionHandler handler_;
    private Protocol protocol_;
    private boolean running_;
    private Logger logger_;

    public SocketServerImpl(Protocol protocol, ChatManager manager) {
	protocol_ 	= protocol;
	running_	= false;
	logger_ 	= SCMPFactory.getLogger(this);
	lobbyUsers_ 	= Collections.synchronizedList(new ArrayList<LobbyUser>());
	config_		= SCMPFactory.getConfiguration();
	acceptor_ 	= new NioSocketAcceptor();
	handler_ 	= new ConnectionHandler();
    }

    @Override
    public void stopServer() {
	logger_.info("SocketServer is shutting down...");
	try {
	    acceptor_.unbind();
	    acceptor_.dispose();
	} catch (Exception ex) {
	    logger_.fatal(ex.getMessage());
	}
	running_ = false;
    }

    @Override
    public List<LobbyUser> getLobbyUsers() {
	return lobbyUsers_;
    }
    @Override
    public Protocol getProtocol() {
	return protocol_;
    }

    @Override
    public void start() throws IOException {
	acceptor_.setReuseAddress(true);
//	acceptor_.getFilterChain().addLast("logger", new LoggingFilter());
	acceptor_.getFilterChain().addLast("pooling", new ExecutorFilter(new OrderedThreadPoolExecutor(4, 25)));
	acceptor_.getFilterChain().addLast("codec", new ProtocolCodecFilter(new XMPPProtocolCodecFactory()));
	acceptor_.getSessionConfig().setReadBufferSize(2048);
	acceptor_.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 6 * 60);
	acceptor_.setHandler(handler_);
	// currently the server IP address is ignored and we are listening to 'any'
	try {
	    acceptor_.bind(new InetSocketAddress(config_.getChatServerPort()));
	    logger_.info("Server is listening on " + config_.getChatServerIP()  + ":" + config_.getChatServerPort()+ "...");
	    running_ = true;
	}catch (BindException e) {
	    logger_.error("Couldn't bind on the specified port:", e);
	    System.exit(1);
	}
    }

    @Override
    public boolean isRunning() {
	return running_;
    }

}
